﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="KeyedXmlRecordTest.cs" company="" />
//
// <summary>
//   Unit tests for class 'KeyedXmlRecord'.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace UtilityUnitTests
{
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Xml.Linq;

    using Microsoft.VisualStudio.TestTools.UnitTesting;

    using Utilities;

    /// <summary>
    /// Unit tests for class 'KeyedXmlRecord'.
    /// </summary>
    [TestClass]
    public class KeyedXmlRecordTest
    {
        // TODO: Add coverage for ignoring name and value case, as well as ignoring white spaces
        #region Constants

        private const string c_elementName1 = "Root1";
        private const string c_attribute1 = "attribute1";
        private const string c_value1 = "value1";

        private const string c_elementName2 = "Root2";
        private const string c_attribute2 = "attribute2";
        private const string c_value2 = "value2";

        private const string c_attribute3 = "attribute3";
        private const string c_value3 = "value3";

        #endregion

        /// <summary>
        /// Test comparison with no keys defined for matching elements.
        /// </summary>
        [TestMethod]
        public void NoKeysMatchingRecordsTest()
        {
            var element1 = new XElement(
                c_elementName1,
                new XAttribute(c_attribute1, c_value1));

            var element2 = new XElement(
                c_elementName1,
                new XAttribute(c_attribute1, c_value1));

            var xDoc1 = new XDocument(element1);
            var xDoc2 = new XDocument(element2);

            var parseTree1 = new ParsedXmlTree(xDoc1);
            var parseTree2 = new ParsedXmlTree(xDoc2);

            var xmlRecord1 = new KeyedXmlRecord(parseTree1, "/" + c_elementName1, element1);
            var xmlRecord2 = new KeyedXmlRecord(parseTree2, "/" + c_elementName1, element2);

            VerifyTestSummary(xmlRecord1, xmlRecord2);
        }

        /// <summary>
        /// Test comparison with no keys defined for non-matching elements.
        /// </summary>
        [TestMethod]
        public void NoKeysNoMatchingRecordsTest()
        {
            var element1 = new XElement(
                c_elementName1,
                new XAttribute(c_attribute1, c_value1));

            var element2 = new XElement(
                c_elementName1,
                new XAttribute(c_attribute1, c_value2));

            var xDoc1 = new XDocument(element1);
            var xDoc2 = new XDocument(element2);

            var parseTree1 = new ParsedXmlTree(xDoc1);
            var parseTree2 = new ParsedXmlTree(xDoc2);

            var xmlRecord1 = new KeyedXmlRecord(parseTree1, "/" + c_elementName1, element1);
            var xmlRecord2 = new KeyedXmlRecord(parseTree2, "/" + c_elementName1, element2);

            Assert.IsFalse(xmlRecord1.Equals(xmlRecord2));
        }

        /// <summary>
        /// 'attribute1' is set as a default key, with matching records.
        /// </summary>
        [TestMethod]
        public void DefaultKeyMatchingRecordsTest()
        {
            var element1 = new XElement(
                c_elementName1,
                new XAttribute(c_attribute1, c_value1),
                new XAttribute(c_attribute2, c_value2));

            var element2 = new XElement(
                c_elementName1,
                new XAttribute(c_attribute1, c_value1),
                new XAttribute(c_attribute2, c_value3));

            var xDoc1 = new XDocument(element1);
            var xDoc2 = new XDocument(element2);

            var parseTree1 = new ParsedXmlTree(xDoc1, c_attribute1);
            var parseTree2 = new ParsedXmlTree(xDoc2, c_attribute1);

            var xmlRecord1 = new KeyedXmlRecord(parseTree1, "/" + c_elementName1, element1);
            var xmlRecord2 = new KeyedXmlRecord(parseTree2, "/" + c_elementName1, element2);

            VerifyTestSummary(xmlRecord1, xmlRecord2, true);
        }

        /// <summary>
        /// 'attribute1' is set as a default key, with non-matching records.
        /// </summary>
        [TestMethod]
        public void DefaultKeyNonMatchingRecordsTest()
        {
            var element1 = new XElement(
                c_elementName1,
                new XAttribute(c_attribute1, c_value1),
                new XAttribute(c_attribute2, c_value1));

            var element2 = new XElement(
                c_elementName1,
                new XAttribute(c_attribute1, c_value2),
                new XAttribute(c_attribute2, c_value2));

            var xDoc1 = new XDocument(element1);
            var xDoc2 = new XDocument(element2);

            var parseTree1 = new ParsedXmlTree(xDoc1, c_attribute1);
            var parseTree2 = new ParsedXmlTree(xDoc2, c_attribute1);

            var xmlRecord1 = new KeyedXmlRecord(parseTree1, "/" + c_elementName1, element1);
            var xmlRecord2 = new KeyedXmlRecord(parseTree2, "/" + c_elementName1, element2);

            Assert.IsFalse(xmlRecord1.Equals(xmlRecord2));
        }

        /// <summary>
        /// 'attribute1' is set as default key, but is ignored since the record is using 'attribute2' as a key. With matching records.
        /// </summary>
        [TestMethod]
        public void OtherKeyMatchingRecordsTest()
        {
            var element1 = new XElement(
                c_elementName1,
                new XAttribute(c_attribute3, c_value2),
                new XAttribute(c_attribute2, c_value1));

            var element2 = new XElement(
                c_elementName1,
                new XAttribute(c_attribute3, c_value3),
                new XAttribute(c_attribute2, c_value1));

            var xDoc1 = new XDocument(element1);
            var xDoc2 = new XDocument(element2);

            var otherKeys = new Dictionary<string, string> { { "/" + c_elementName1, c_attribute2 } };
            var parseTree1 = new ParsedXmlTree(xDoc1, c_attribute1, otherKeys);
            var parseTree2 = new ParsedXmlTree(xDoc2, c_attribute1, otherKeys);

            var xmlRecord1 = new KeyedXmlRecord(parseTree1, "/" + c_elementName1, element1);
            var xmlRecord2 = new KeyedXmlRecord(parseTree2, "/" + c_elementName1, element2);

            VerifyTestSummary(xmlRecord1, xmlRecord2, true);
        }

        /// <summary>
        /// 'attribute1' is set as default key, but is ignored since the record is using 'attribute2' as a key. With non-matching records.
        /// </summary>
        [TestMethod]
        public void OtherKeyNonMatchingRecordsTest()
        {
            var element1 = new XElement(
                c_elementName1,
                new XAttribute(c_attribute3, c_value3),
                new XAttribute(c_attribute2, c_value1));

            var element2 = new XElement(
                c_elementName1,
                new XAttribute(c_attribute3, c_value1),
                new XAttribute(c_attribute2, c_value2));

            var xDoc1 = new XDocument(element1);
            var xDoc2 = new XDocument(element2);

            var otherKeys = new Dictionary<string, string> { { "/" + c_elementName1, c_attribute2 } };
            var parseTree1 = new ParsedXmlTree(xDoc1, c_attribute1, otherKeys);
            var parseTree2 = new ParsedXmlTree(xDoc2, c_attribute1, otherKeys);

            var xmlRecord1 = new KeyedXmlRecord(parseTree1, "/" + c_elementName1, element1);
            var xmlRecord2 = new KeyedXmlRecord(parseTree2, "/" + c_elementName1, element2);

            Assert.IsFalse(xmlRecord1.Equals(xmlRecord2));
        }

        [TestMethod]
        public void DebugTest()
        {
            var result = ParsedXmlTree.SplitPath("/a");

            return;

            var document1 = XDocument.Load(@"C:\Users\adishilo\Desktop\server-side.xaml");
            var parsedTree1 = new ParsedXmlTree(document1);

            var document2 = XDocument.Load(@"C:\Users\adishilo\Desktop\local-side.xaml");
            var parsedTree2 = new ParsedXmlTree(document2);

            var comparer = new CollectionComparer<string, ComparableSet<KeyedXmlRecord>>(parsedTree1.TreeRecords, parsedTree2.TreeRecords);

            Trace.TraceInformation("Only original:");
            foreach (var original in comparer.OnlyOriginal)
            {
                Trace.TraceInformation("{0}", original.Value);
            }

            Trace.TraceInformation("Only target:");
            foreach (var target in comparer.OnlyTarget)
            {
                Trace.TraceInformation("{0}", target.Value);
            }

            Trace.TraceInformation("Differences - Original:");
            foreach (var diff in comparer.Differences)
            {
                foreach (var original in diff.Value.Item1)
                {
                    Trace.TraceInformation("{0}", original);
                }
            }

            Trace.TraceInformation("Differences - Target:");
            foreach (var diff in comparer.Differences)
            {
                foreach (var target in diff.Value.Item2)
                {
                    Trace.TraceInformation("{0}", target);
                }
            }
        }

        #region Private Helpers

        /// <summary>
        /// Verified required KPIs for the <see cref="KeyedXmlRecord"/> tests.
        /// </summary>
        /// <param name="xmlFirstRecord">First record to verify in comparison.</param>
        /// <param name="xmlSecondRecord">Second record to verify in comparison.</param>
        /// <param name="expectHashCodeDifferent">Whether to expect the hash-code to be different in both.</param>
        private void VerifyTestSummary(
            KeyedXmlRecord xmlFirstRecord,
            KeyedXmlRecord xmlSecondRecord,
            bool expectHashCodeDifferent = false)
        {
            Assert.IsTrue(xmlFirstRecord.Equals(xmlSecondRecord), "KeyedXmlRecord.Equals()");
            Assert.IsTrue(
                (xmlFirstRecord.Key == null && xmlSecondRecord.Key == null) ||
                xmlFirstRecord.Key.Equals(xmlSecondRecord.Key),
                "KeyedXmlRecord.Key");
            Assert.IsTrue(xmlFirstRecord.KeyValue.Equals(xmlSecondRecord.KeyValue), "KeyedXmlRecord.KeyValue");
            Assert.IsTrue(xmlFirstRecord.RecordPath.Equals(xmlSecondRecord.RecordPath), "KeyedXmlRecord.RecordPath");

            if (expectHashCodeDifferent)
            {
                Assert.AreNotEqual(xmlFirstRecord.GetHashCode(), xmlSecondRecord.GetHashCode(), "KeyedXmlRecord.GetHashCode() expected not equal.");
            }
            else
            {
                Assert.AreEqual(xmlFirstRecord.GetHashCode(), xmlSecondRecord.GetHashCode(), "KeyedXmlRecord.GetHashCode() expected equal.");
            }
        }

        #endregion
    }
}
